<?php

class RARAR {

    function RARAR($origin, $destination, $date, $time) {
        $this->origin = $origin;
        $this->destination = $destination;
        $this->date = $date;
        $this->time = $time;
        
        //Connection values
        $this->uname = 'langerkanniet@gmail.com';
        $this->pwd = 'IAoV4OhtZC9LnxYDhn9HCp8TtAnd3-wlfca_wriQQctaosaUjyaegg';
    }

    // access Google Directions API with destination and origin and give an array with all steps back
    function plan_route() {
        $googleurl = "http://maps.googleapis.com/maps/api/directions/json?origin=" . $this->origin . "&destination=" . $this->destination . "&sensor=false";
        $google = file_get_contents($googleurl, 0, null, null);
        $google_output = json_decode($google);
        // store all coordinates from start and end position of each way step into a new array
        $route = array();
	if(sizeof($google_output->routes[0]->legs[0]->steps)>0) {
        foreach ($google_output->routes[0]->legs[0]->steps as $step) {
            $entry = array(
                "startlat" => $step->start_location->lat,
                "endlat" => $step->end_location->lat,
                "startlng" => $step->start_location->lng,
                "endlng" => $step->end_location->lng,
            );
            array_push($route, $entry);
        }
	}
        return $route;
    }

    // access Google Directions API and give length of route back. If a waypoint is specified, the route with the waypoint is caluclated
    function get_routelength($waypoint = "") {
        $googleurl = "http://maps.googleapis.com/maps/api/directions/json?origin=" . $this->origin . "&destination=" . $this->destination . "&sensor=false";
        if (strlen($waypoint) > 0)
            $googleurl.="&waypoints=" . $waypoint;
        $google = file_get_contents($googleurl, 0, null, null);
        $google_output = json_decode($google);
        $length = 0;
	while($google_output->status=="OVER_QUERY_LIMIT") {
        $google = file_get_contents($googleurl, 0, null, null);
        $google_output = json_decode($google);
	}
	if(sizeof($google_output->routes[0]->legs[0]->steps)>0) {
        foreach ($google_output->routes[0]->legs as $leg) {
            $length+=$leg->distance->value;
        }}
        return $length;
    }

    // catch all POIs from GeoNames which fit to the deliverd featurecodes (array)
    function get_pois($featurecodes = "") {
        // create the arguments for the API call with the featureCodes, which have been clicked in the form
        $args = "";
        if ($featurecodes != "") {
            foreach ($featurecodes as $fc) {
                $args.="&featureCode=" . $fc;
            }
        }

        // access GeoNames API with featurecodes
        $geonamesurl = "http://api.geonames.org/search?username=mmabc&country=NL" . $args;
        $geonames = file_get_contents($geonamesurl, 0, null, null);
        $geonames_output = new SimpleXMLElement($geonames);

        // store all POIs with their coordinates into a new array
        $pois = array();
        foreach ($geonames_output->children() as $poi) {
            $entry = array(
                "name" => (string) $poi->name,
                "lat" => (string) $poi->lat,
                "lng" => (string) $poi->lng,
                "fcl" => (string) $poi->fcl,
                "fcode" => $this->translateFeatureCode((string) $poi->fcode));
            if (strlen($poi->name) > 0)
                array_push($pois, $entry);
        }
        return $pois;
    }

    // calculate distance betweeen two latitude/longitude coordinates
    function distance_slc($lat1, $lon1, $lat2, $lon2) {
        $earth_radius = 6371.00;
        $delta_lat = $lat2 - $lat1;
        $delta_lon = $lon2 - $lon1;
        $alpha = $delta_lat / 2;
        $beta = $delta_lon / 2;
        $a = sin(deg2rad($alpha)) * sin(deg2rad($alpha)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin(deg2rad($beta)) * sin(deg2rad($beta));
        $c = asin(min(1, sqrt($a)));
        $distance = 2 * $earth_radius * $c;
        $distance = round($distance, 4);
        return $distance;
    }

    // find for each POI the closest waypoint of the route, sort it by this distance and give 5 best POIs back 
    function find_best_pois($featurecodes) {
        $route = $this->plan_route();
        $pois = $this->get_pois($featurecodes);
        // for all POIs
        foreach ($pois as $poikey => $poi) {
            $min_distance = 0;
            $min_key = 0;
            // for all route points
            foreach ($route as $key => $step) {
                $distance = $this->distance_slc($step[startlat], $step[startlng], $poi[lat], $poi[lng]);
                if ($min_distance == 0 || $min_distance > $distance) {
                    $min_distance = $distance;
                    $min_key = $key;
                }
            }

            // calculate direct way and way from last waypoint to POI + way from POI to next waypoint	
            $direct = $this->distance_slc($route[$min_key][startlat], $route[$min_key][startlng], $route[$min_key][endlat], $route[$min_key][endlng]);
            $detour1 = $this->distance_slc($route[$min_key][startlat], $route[$min_key][startlng], $poi[lat], $poi[lng]);
            $detour2 = $this->distance_slc($poi[lat], $poi[lng], $route[$min_key][endlat], $route[$min_key][endlng]);

            // calculate the additional way	
            $moreway = $detour1 + $detour2 - $direct;

            // do the same not only for the route to the next waypoint but also for the route to the waypoint before (if there is one)
            if ($min_key > 0) {
                $direct = $this->distance_slc($route[$min_key - 1][startlat], $route[$min_key - 1][startlng], $route[$min_key - 1][endlat], $route[$min_key - 1][endlng]);
                $detour1 = $this->distance_slc($route[$min_key - 1][startlat], $route[$min_key - 1][startlng], $poi[lat], $poi[lng]);
                $detour2 = $this->distance_slc($poi[lat], $poi[lng], $route[$min_key - 1][endlat], $route[$min_key - 1][endlng]);
                $moreway2 = $detour1 + $detour2 - $direct;

                // choose the shorter route
                if ($moreway2 < $moreway) {
                    $min_key--;
                    $moreway = $moreway2;
                }
            }
            $pois[$poikey][moreway] = $moreway;
        }

        // sort pois according to the way, you have to make more in order to reach them
        foreach ($pois as $c => $poi)
            $sort_moreway[] = $poi[moreway];

        array_multisort($sort_moreway, SORT_ASC, $pois);

        // return best 5 POIs
        return array_slice($pois, 0, 5);
    }

    // main function, calls the other functions, checks the Google route for the 5 best POIs
    function get_result($featurecodes) {
        $best_pois = $this->find_best_pois($featurecodes);

        // length of way without stop
        $directlength = $this->get_routelength();

        // check Google API for length of way for each of the 5 best waypoints
        foreach ($best_pois as $key => $poi) {
            $wp = $poi[lat] . "," . $poi[lng];
		$plus = $this->get_routelength($wp);
            $length = ($plus - $directlength);
            $best_pois[$key][length] = $length;
        }

        // sort by route length
        foreach ($best_pois as $c => $poi)
            $sort_length[] = $poi[length];
        array_multisort($sort_length, SORT_ASC, $best_pois);

        // return the ordered list of best points with the final best one at the first position
        return $best_pois;
    }

    //Connect to a specific url, returns a xml result
    public function connect($urlInput) {
        //Make the call to the ns api
        $ch = curl_init($urlInput);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
        curl_setopt($ch, CURLOPT_USERPWD, $this->uname . ':' . $this->pwd);
        $response = curl_exec($ch);
        curl_close($ch);
        return new SimpleXMLElement($response);
    }

    /* Check if the train webservice gave an error.
     * If so throw a exception.
     */

    public function checkTrainError($xmlInput) {
        if ($xmlInput->message == 'Alleen reisadviezen van en naar een station worden ondersteund') {
            //Start or Endpoint is no station
            throw new Exception('Train advises can only be given for starting and endpoints with trainstations.');
        }
    }

    //Finds the quickest train possibility returns a ReisMogelijkheid xml
    //WARNING: this is the old version. Left here for historical purposes.
    //The new version is below.
//    public function getQuickest($xmlInput) {
//        $index = 0;
//        $lowest = strtotime($xmlInput->ReisMogelijkheid[0]->GeplandeReisTijd);
//
//        for ($i = 1; $i > 5; $i++) {
//            if ($lowest > strtotime($xmlInput->ReisMogelijkheid[$i]->GeplandeReisTijd)) {
//                $index = $i;
//                $lowest = strtotime($xmlInput->ReisMogelijkheid[$i]->GeplandeReisTijd);
//            }
//        }
//
//        return $xmlInput->ReisMogelijkheid[$index];
//    }

    /* Returns the index of the quickest route
     */
    public function getQuickest($xmlInput) {
        $this->checkTrainError($xmlInput);

        $index = 0;
        $lowest = strtotime($xmlInput->ReisMogelijkheid[0]->GeplandeReisTijd);

        for ($i = 1; $i > 5; $i++) {
            if ($lowest > strtotime($xmlInput->ReisMogelijkheid[$i]->GeplandeReisTijd)) {
                $index = $i;
                $lowest = strtotime($xmlInput->ReisMogelijkheid[$i]->GeplandeReisTijd);
            }
        }

        return $index;
    }

    /* Input: xml from the train webservice, the index of the possibility you 
     * want to display. The webservice returns 5 possibilities, so the index has
     *  to be a number between 0 and 4.
     * 
     * Returns a 2d array filled with the stops the quickest journey has.
     * The array contain sub-arrays that have the following info:
     * [0] Name of the station
     * [1] Time of departure
     * [2] Which platform the train arrives
     */

    public function getStops($xmlInput, $index) {
        $this->checkTrainError($xmlInput);
        //First determine the quickest journey of the 5 given suggestions
        //$journey = $this->getQuickest($xmlInput);

        $result = array();

        //Now extract the stops
        //foreach ($journey->ReisDeel as $part) {
        foreach ($xmlInput->ReisMogelijkheid[$index]->ReisDeel as $part) {
            if ($part["reisSoort"] == "TRAIN") {
                //Get the name of each stop
                foreach ($part->ReisStop as $stop) {
                    $traininfo = array(trim((string) $stop->Naam), trim((string) $stop->Tijd), trim((string) $stop->Spoor));
                    $result[] = $traininfo;
                }
            }
        }

        return $result;
    }

    public function getTrainconnection() {
        //Set up vars for getting train information
        $trainDomain = 'http://webservices.ns.nl';
        $trainService = '/ns-api-treinplanner?';
        $trainUrl = $trainDomain . $trainService . 'fromStation=' . rawurlencode($this->origin) . '&toStation=' . rawurlencode($this->destination);
        $trainUrl .= '&dateTime=' . rawurlencode(date_format(new DateTime($this->date), 'Y-m-d')) . 'T' . rawurlencode($this->time) . rawurlencode('+0100');
        
        //Get the stops from the train
        $xml = $this->connect($trainUrl);
        $quickest = $this->getQuickest($xml);
        $trainStops = $this->getStops($xml, $quickest);
        return $trainStops;
    }

    public function get_railresult($trainStops, $featurecodes) {
        foreach ($trainStops as $station) {
            $geonamesurl = "http://api.geonames.org/search?username=mmabc&country=NL&featureCode=RSTP&featureCode=RSTN&q=" . $station[0];
            $geonames = file_get_contents($geonamesurl, 0, null, null);
            $geonames_output = new SimpleXMLElement($geonames);
            $entry = array((string) $geonames_output->geoname->name, (double) $geonames_output->geoname->lat, (double) $geonames_output->geoname->lng);
            $stations[] = $entry;
        }
        
        $this->stations = $stations;

        $pois = $this->get_pois($featurecodes);
        // for all POIs
        foreach ($pois as $poikey => $poi) {
            $min_distance = 0;
            $min_key = 0;
            // for all route points
	    if(sizeof($station)>0) {
            foreach ($stations as $key => $station) {
                $distance = $this->distance_slc($station[1], $station[2], $poi[lat], $poi[lng]);
                if ($min_distance == 0 || $min_distance > $distance) {
                    $min_distance = $distance;
                    $min_key = $key;
                }
                $pois[$poikey][moreway] = $min_distance;
                $pois[$poikey][beststation] = $min_key;
            }}
        }
        // sort pois according to the way, you have to make more in order to reach them
        foreach ($pois as $c => $poi)
            $sort_moreway[] = $poi[moreway];

        array_multisort($sort_moreway, SORT_ASC, $pois);
        // return best 5 POIs
        return array_slice($pois, 0, 5);
    }
    
    /* Translate a feature code in a more human friendly name
     * Returns a string.
     */

    public function translateFeatureCode($fcode) {
        $output = '';

        switch ($fcode) {
            case 'AMUS':
                $output = 'Amusement Park';
                break;
            case 'MNMT':
                $output = 'Monument';
                break;
            case 'MUS':
                $output = 'Museum';
                break;
            case 'OBPT':
                $output = 'Observation point';
                break;
            case 'HSTS':
                $output = 'Historical Site';
                break;
            case 'PRK':
                $output = 'Park';
                break;
            case 'MNMT':
                $output = 'Monument';
                break;
            case 'ZOO':
                $output = 'Zoo';
                break;
            case 'CH':
                $output = 'Church';
                break;
            case 'STDM':
                $output = 'Stadium';
                break;
            case 'CSTL':
                $output = 'Castle';
                break;
        }

        return $output;
    }
    
    /*Returns a array with long and lat for a station*/
    public function getStationLatLong($key) {
        $output = array();
	$stations=$this->stations;
                $output[0] = $stations[$key][1];
                $output[1] = $stations[$key][2];
		$output[2] = $stations[$key][0];
        return $output;
    }

}

?>
